#include "giza.h"

CGiza::CGiza()
{
	num_sent = 0;
}


CGiza::~CGiza()
{
	num_sent = 0;
}

bool CGiza::Open( char fn[] )
{
	if( file.IsOpen() )	file.CloseFile();

	file.OpenFile( fn, "r" );
	if( file.IsOpen() )	return true;
	else				return false;
}


bool CGiza::CheckOpen( char fn[] )
{
	if( file.IsOpen() == true )	{
		file.CloseFile();
	}

	fprintf( stdout, "Open giza file [%s] ... ", fn );
	file.OpenFile( fn, "r" );
	if( file.IsOpen() == false )	{
		fprintf( stdout, "[fail]\n" );
		return true;
	}

	fprintf( stdout, "[OK]\n" );
	return true;
}


bool CGiza::LoadTgt( CUString &str )
{
	tgt.tok_list.TokenizeByChar( str, " ", false );
	return true;
}

bool CGiza::LoadSrcAlign( CUString &str )
{
	align.align_list.Clear();

	src.tok_list.TokenizeByStr( str, "})", false );
	for( int i=0; i<src.tok_list.GetSize(); i++ )	{
		CUString src_str = src.tok_list.GetAt(i);
		int idx = src_str.Find("({" );
		if( idx < 0 )	{
			fprintf( stderr, "invalid align file ({: %s\n", src_str.GetStr()  );
			return false;
		}

		CUString align_str = src_str.Mid( idx+2 ); align_str.Trim();
		src_str = src_str.Left(idx ); src_str.Trim();

		src.tok_list.SetAt( i, src_str );
		align.align_list.PushBack( align_str );
	}

	if( src.tok_list.GetSize() != align.align_list.GetSize() )	{
		fprintf( stderr, "diff src and align\n" );

		for( int i=0; i<src.tok_list.GetSize(); i++ )	{
			fprintf( stderr, "[%d] %s\n", i, src.tok_list.GetAt(i) );
		}
		for( int i=0; i<align.align_list.GetSize(); i++ )	{
			fprintf( stderr, "[%d] %s\n", i, align.align_list.GetAt(i) );
		}

		return false;
	}

	return true;
}

void CGiza::SwapList( CUStringList &src_list, int i, int j )
{
	CUString str = src_list.GetAt(i);
	src_list.SetAt(i, src_list.GetAt(j) );
	src_list.SetAt(j, str );
}


bool CGiza::ReadSent()
{
	if( file.IsOpen() == false )	{
		fprintf( stderr, "Giza is not opened\n" );
		return false;
	}

	if( file.ReadLine( score ) == false )	{
//		fprintf( stderr, "giza file early-end\n" );
		return false;
	}
	score.Replace("\r", "" ); score.Trim("\t ");

	if( score.Find( "# Sentence pair " ) == 0 )	{
		CUString tgt;
		if( file.ReadLine( tgt ) == false )	return false;
		tgt.Replace("\r", "" ); tgt.Trim("\t ");
		if( LoadTgt( tgt ) == false )	{
			fprintf( stderr, "fail to load tgt\n" );
			return false;
		}

		CUString src;
		if( file.ReadLine( src ) == false )	{
			fprintf( stderr, "giza file early-end\n" );
			return false;
		}

		src.Replace("\r", "" ); src.Trim("\t ");
		if( LoadSrcAlign( src ) == false )	{
			fprintf( stderr, "fail to load src align\n" );
			return false;
		}
	}
	else	{

		CUString tgt = score;
		char buf[1024];
		sprintf( buf, "# Sentence pair (%d)", num_sent+1 );
		score = buf;
		if( LoadTgt( tgt ) == false )	{
			fprintf( stderr, "fail to load tgt\n" );
			return false;
		}

		CUString src;
		if( file.ReadLine( src ) == false )	{
			fprintf( stderr, "giza file early-end\n" );
			return false;
		}

		src.Replace("\r", "" ); src.Trim("\t ");
		if( LoadSrcAlign( src ) == false )	{
			fprintf( stderr, "fail to load src align\n" );
			return false;
		}

	}

	num_sent++;
	return true;
}

void SwapTgt( CUStringList &alist, int id1, int id2 )
{
	char str1[1024], str2[1024];
	sprintf( str1, "%d", id1 );
	sprintf( str2, "%d", id2 );

	// detect chage idx
	for( int i=0; i<alist.GetSize(); i++ )	{
		CUString str = alist.GetAt(i);
		CUStringList ls; ls.TokenizeByChar( str, " ", false );
		for( int j=0; j<ls.GetSize(); j++ )	{
			CUString id = ls.GetAt(j);
			if( id == str1 )	ls.SetAt( j, "ID1" );
			if( id == str2 )	ls.SetAt( j, "ID2" );
		}
		CUString buf = ls.ToString();
		alist.SetAt(i, buf );
	}

	// reorder
	for( int i=0; i<alist.GetSize(); i++ )	{
		CUString str = alist.GetAt(i);
		CUStringList ls; ls.TokenizeByChar( str, " ", false );
		for( int j=0; j<ls.GetSize(); j++ )	{
			CUString id = ls.GetAt(j);
			if( id == "ID1" )	{
				ls.SetAt( j, str2 );
			}
			if( id == "ID2" )	{
				ls.SetAt( j, str1 );
			}
		}
		CUString buf = ls.ToString();
		alist.SetAt(i, buf );
	}
}

bool CGiza::Reorder( CUStringList &info_list, char lang )
{
	/*
	info_list.PrintEx( stdout, " " );
	fprintf( stdout, "\n" );
	src.tok_list.PrintEx( stdout, " " );
	fprintf( stdout, "\n" );
	tgt.tok_list.PrintEx( stdout, " " );
	fprintf( stdout, "\n" );
	align.align_list.PrintEx( stdout, " " );
	fprintf( stdout, "\n" );
	fflush( stdout );
	*/


	for( int i=0; i<info_list.GetSize()-1; i++ )	{
		CUString id1 = info_list.GetAt(i);
		for( int j=i+1; j<info_list.GetSize(); j++ )	{
			CUString id2 = info_list.GetAt(j);

			if( atoi(id1.GetStr() ) > atoi(id2.GetStr() ) )	{

				info_list.SetAt(i, id2 );
				info_list.SetAt(j, id1 );

	//			fprintf( stdout, "%s %s\n", id1.GetStr(), id2.GetStr() );
				if( lang == 'S' )	{
					if( i+1 >= src.tok_list.GetSize() || j+1 >= src.tok_list.GetSize() )	{
						fprintf( stderr, "invalid info file (too long sentence)\n" );
						return false;
					}
					SwapList( src.tok_list, i+1, j+1 );

					if( i+1 >= align.align_list.GetSize() || j+1 >= align.align_list.GetSize() )	{
						fprintf( stderr, "invalid info file (too long sentence)\n" );
						return false;
					}
					SwapList( align.align_list, i+1, j+1 );
				}
				else if( lang == 'T' )	{
					SwapList( tgt.tok_list, i, j );
					SwapTgt( align.align_list, i+1 ,j+1 );
				}


				CUString tmp = id1;
				id1 = id2;
				id2 = tmp;

			}
		}
	}

	/*
	src.tok_list.PrintEx( stdout, " " );
	fprintf( stdout, "\n" );
	tgt.tok_list.PrintEx( stdout, " " );
	fprintf( stdout, "\n" );
	fflush( stdout );
	align.align_list.PrintEx( stdout, " " );
	fprintf( stdout, "\n" );
	fprintf( stdout, "\n" );
	*/
	return true;
}

bool CGiza::DeleteInsertedWords( CUStringList &src_list, char lang )
{
	if( src_list.GetSize()+1 == src.tok_list.GetSize() )	{
		return true;
	}

	if( src_list.GetSize() == 0 )	{
		return true;
	}

	/*
	for( int i=src.tok_list.GetSize()-1; i>=1; i-- )	{
		CUString giza_src = src.tok_list.GetAt(i);
		fprintf( stdout,"%s ", giza_src.GetStr() );
	}
	fprintf( stdout,"\n" );

	for( int i=src_list.GetSize()-1; i>=0; i-- )	{
		CUString org_src = src_list.GetAt(i);
		fprintf( stdout,"%s ", org_src.GetStr() );
	}
	fprintf( stdout,"\n\n" );
	*/


	/*
	for( int i=1; i<src.tok_list.GetSize(); i++ )	{
		CUString giza_src = src.tok_list.GetAt(i);
		fprintf( stdout,"%s ", giza_src.GetStr() );
	}
	fprintf( stdout,"\n" );

	for( int i=0; i<src_list.GetSize(); i++ )	{
		CUString org_src = src_list.GetAt(i);
		fprintf( stdout,"%s ", org_src.GetStr() );
	}
	fprintf( stdout,"\n\n" );
	*/

	int org_idx = src_list.GetSize()-1;
	CUString org = src_list.GetAt( org_idx );

	/*
	src.tok_list.PrintEx( stdout ); fprintf( stdout,"\n"); fflush( stdout );
	align.align_list.PrintEx( stdout ); fprintf( stdout,"\n"); fflush( stdout );
	*/
	for( int i=src.tok_list.GetSize()-1; i>=1; i-- )	{
		CUString giza_src = src.tok_list.GetAt(i);

		if( giza_src == org )	{
			//fprintf( stdout, "m:%s ", giza_src.GetStr() ); fflush( stdout );

			if( org_idx > 0 )	{
				org_idx--;
				org = src_list.GetAt( org_idx );
			}
		}
		else	{
			//fprintf( stdout, "u:%s ", giza_src.GetStr() ); fflush( stdout );

			//delete inserted words and their alignments
			for( int j=i; j<src.tok_list.GetSize()-1; j++ )	{
				src.tok_list.SetAt( j, src.tok_list.GetAt( j+1 ) );
				align.align_list.SetAt( j, align.align_list.GetAt( j+1 ) );
			}
			src.tok_list.RemoveBack();
			align.align_list.RemoveBack();
		}

	}
	/*
	fprintf( stdout, "\n\n" ); fflush( stdout );

	src.tok_list.PrintEx( stdout ); fprintf( stdout,"\n"); fflush( stdout );
	align.align_list.PrintEx( stdout ); fprintf( stdout,"\n"); fflush( stdout );
	fprintf( stdout, "\n\n" ); fflush( stdout );
	*/


	return true;
}

bool CGiza::RecoverInsert( char src_fn[], char out_fn[], char lang )
{
	if( file.IsOpen() == false )	{
		fprintf( stderr, "Giza is not opened\n" );
		return false;
	}

	CUTextFile src_file( src_fn, "r" );
	if( src_file.CheckOpen() == false )	return false;

	CUTextFile out_file( out_fn, "w" );
	if( out_file.CheckOpen() == false )	return false;

	int num_sent = 0;
	while(1)	{
		CUString src;
		if( src_file.ReadLine( src ) == false )	break;

		// giza read
		if( ReadSent() == false )	break;
		num_sent++;

		CUStringList src_list;
		src_list.TokenizeByChar( src, " ", false );

		if( DeleteInsertedWords( src_list ) == false )	{
			fprintf( stderr, "%d\n", num_sent );
			return false;
		}

		SaveOut( out_file.GetFP() );
		fflush ( out_file.GetFP() );
	}

	return true;
}

bool CGiza::RecoverReorder( char info_fn[], char out_fn[], char lang )
{
	if( file.IsOpen() == false )	{
		fprintf( stderr, "Giza is not opened\n" );
		return false;
	}

	CUTextFile info_file( info_fn, "r" );
	if( info_file.CheckOpen() == false )	return false;

	CUTextFile out_file( out_fn, "w" );
	if( out_file.CheckOpen() == false )	return false;

	int num_sent = 0;
	while(1)	{
		CUString info;
		// info read
		if( info_file.ReadLine( info ) == false )	break;

		// giza read
		if( ReadSent() == false )	break;
		num_sent++;

		// un-reordered
		if( info == "ORG" )	{
		}
		else	{
			CUStringList info_list;
			info_list.TokenizeByChar( info, " ", false );
			/*
			src.tok_list.Print( out_file.GetFP() );
			align.align_list.Print( out_file.GetFP() );
			fprintf( out_file.GetFP(), "\n%s\n", info.GetStr() );
			fflush ( out_file.GetFP() );
			*/
		//	if( num_sent != 11 )	continue;

			if( Reorder( info_list, lang ) == false )	{
				fprintf( stderr, "%d\n", num_sent );
				return false;
			}
			SaveOut( out_file.GetFP() );
			fflush ( out_file.GetFP() );
		}
	}

	return true;
}

bool CGiza::RecoverKor( CUString &kinfo )
{
	for( int i=0; i<tgt.tok_list.GetSize(); i++ )	{
		CUString str = tgt.tok_list.GetAt(i);
		char tmp[1024];
		sprintf( tmp, "%d\t%s", i+1, str.GetStr() );
		tgt.tok_list.SetAt( i, tmp );
	}


	CUStringList kinfo_list;
	kinfo_list.TokenizeByChar( kinfo, "\t", false );
	for( int i=0; i<kinfo_list.GetSize(); i++ )	{

		CUStringList tok_list;
		tok_list.TokenizeByChar( kinfo_list.GetAt(i), " ", false );

		int id = atoi( tok_list.GetAt(0) );
		for( int j=1; j<tok_list.GetSize(); j+=2 )	{
			CUString org = tgt.tok_list.GetAt(id);
			org += CUString(" ") + tok_list.GetAt(j) + CUString("/") + tok_list.GetAt(j+1);
			tgt.tok_list.SetAt( id, org );
		}
	}


	int diff[1000]; for( int i=0; i<1000; i++ ) diff[i] = 0;
	int t=0;
	for( int i=0; i<tgt.tok_list.GetSize(); i++ )	{
		CUString tok_str = tgt.tok_list.GetAt(i);

		CUStringList tok_list;
		tok_list.TokenizeByChar( tok_str, " ", false );
		for( int j=0; j<tok_list.GetSize(); j++ )	{

			t++;
			CUString tmp_str = tok_list.GetAt(j);
			int idx = tmp_str.Find("\t");
			if( idx < 0 )	continue;

			int org_id = atoi( tmp_str.Left( idx ).GetStr()  );
			diff[org_id] =  t;
		}
	}



	for( int i=0; i<tgt.tok_list.GetSize(); i++ )	{
		CUString str = tgt.tok_list.GetAt(i);
		str = str.Mid( str.Find("\t" )+1 );
		tgt.tok_list.SetAt(i, str );
	}

	// recover offset
	for( int i=0; i<src.tok_list.GetSize(); i++ )	{
		CUString src_str = src.tok_list.GetAt(i); src_str.Trim();
		CUString align_str = align.align_list.GetAt(i); align_str.Trim();

		CUStringList align_list;
		CUString rec_str;
		align_list.TokenizeByChar( align_str, " ", false );
		for( int j=0; j<align_list.GetSize(); j++ )	{
			int id = atoi( align_list.GetAt(j) );
			char buf[1024];
			sprintf( buf, " %d", diff[id] );
			rec_str += buf;
		}

		rec_str.Trim();
		align.align_list.SetAt(i, rec_str );
	}
}

bool CGiza::RecoverKor( char kinfo_fn[], char out_fn[] )
{
	if( file.IsOpen() == false )	{
		fprintf( stderr, "Giza is not opened\n" );
		return false;
	}

	CUTextFile kinfo_file( kinfo_fn, "r" );
	if( kinfo_file.CheckOpen() == false )	return false;

	CUTextFile out_file( out_fn, "w" );
	if( out_file.CheckOpen() == false )	return false;

	while(1)	{
		CUString kinfo;
		if( kinfo_file.ReadLine( kinfo ) == false )	break;
		if( ReadSent() == false )	break;

		RecoverKor( kinfo );
		SaveOut( out_file.GetFP() );
	}

	return true;
}


void CGiza::SaveOut( FILE *fp )
{
	if( fp == NULL ) 	return;

	fprintf( fp, "%s\n", score.GetStr() );
	for( int i=0; i<tgt.tok_list.GetSize(); i++ )	{
		if( i > 0 )	{
			fprintf( fp, " " );
		}
		fprintf( fp, "%s", tgt.tok_list.GetAt(i) );

	}
	fprintf( fp, " \n" );

	for( int i=0; i<src.tok_list.GetSize(); i++ )	{
		if( i!=0 )	{
			fprintf( fp, " " );
		}

		fprintf( fp, "%s ({", src.tok_list.GetAt(i) );

		CUStringList tok;
		tok.TokenizeByChar( align.align_list.GetAt(i), " ", false );
		for( int j=0; j<tok.GetSize(); j++ )	{
			int id = atoi( tok.GetAt(j) );
			fprintf( fp, " %d", id );
		}
		fprintf( fp, " })" );
	}
	fprintf( fp, " \n" );

}


